<template>
  <view class="ve-tabs" id="ve-tabs">
    <scroll-view scroll-x :scroll-left="scrollLeft" scroll-with-animation class="scroll-view">
      <view class="ve-tabs-list">
        <!-- tab-item -->
        <view
          v-for="(item, index) in list"
          :id="'ve-tabs-item-' + index"
          :key="index"
          @tap="selectTab(index, item)"
          class="ve-tabs-item"
          :class="{ 'is-actived': activedIndex === index }"
        >
          <text class="text" v-if="item.label">{{ item.label }}</text>
          <text class="text" v-else>{{ item }}</text>
          <view v-if="item.num" class="badge" :style="badgeStyle">
            {{ item.num > 999 ? '999+' : item.num }}
          </view>
        </view>
        <!-- 底部条 -->
        <view class="ve-tabs-bar" :style="[tabBarStyle]"></view>
      </view>
    </scroll-view>
  </view>
</template>

<script>
  export default {
    props: {
      list: {
        type: Array,
        default: () => [],
      },
      actived: {
        type: Number,
        default: 0,
      },
      showBorder: {
        type: Boolean,
        default: false,
      },
      badgeStyle: {
        type: Object,
        default: () => {},
      },
    },
    data() {
      return {
        activedIndex: 0, // 激活的下标
        componentWidth: 0, // 屏幕宽度，单位为px
        scrollLeft: 0, // 滚动scroll-view的左边滚动距离
        scrollBarLeft: 0, // 移动bar需要通过translateX()移动的距离
        parentLeft: 0, // 父元素(tabs组件)到屏幕左边的距离
        barFirstTimeMove: true, // 滑块第一次移动时(页面刚生成时)，无需动画，否则给人怪异的感觉

        initied: false,
      }
    },
    watch: {
      actived: {
        handler(newVal, oldVal) {
          if (newVal != oldVal) {
            this.activedIndex = newVal
            if (this.initied) {
              // 延时定时器保证页面跳转完成之后再执行
              setTimeout(() => {
                this.scrollByIndex()
              }, 0)
            }
          }
        },
        immediate: true,
      },
    },
    computed: {
      // 移动bar的样式
      tabBarStyle() {
        let style = {
          transform: `translateX(${this.scrollBarLeft}px)`,
          // 滑块在页面渲染后第一次滑动时，无需动画效果
          'transition-duration': `${this.barFirstTimeMove ? 0 : 0.25}s`,
          opacity: this.barFirstTimeMove ? 0 : 1,
        }
        return style
      },
    },
    mounted() {
      setTimeout(() => {
        this.init()
      }, 0)
    },
    methods: {
      // 初始化tabs宽度 和每一个item的宽度
      init() {
        const query = uni.createSelectorQuery().in(this)
        query
          .select('#ve-tabs')
          .boundingClientRect((tabRect) => {
            // tabs组件距离屏幕左边的宽度
            this.parentLeft = tabRect.left
            // tabs组件的宽度
            this.componentWidth = tabRect.width
            this.getTabRect()
            this.initied = true
          })
          .exec()
      },
      getTabRect() {
        // 创建节点查询
        const query = uni.createSelectorQuery().in(this)
        // 历遍所有tab，这里是执行了查询，最终使用exec()会一次性返回查询的数组结果
        for (let i = 0; i < this.list.length; i++) {
          // 只要size和rect两个参数
          query.select(`#ve-tabs-item-${i}`).fields({
            size: true,
            rect: true,
          })
        }
        // 执行查询，一次性获取多个结果
        query.exec(
          function (res) {
            this.tabAllInfo = res
            // 初始化滚动条和移动bar的位置
            this.scrollByIndex()
          }.bind(this),
        )
      },
      // 选择tab
      selectTab(index, item) {
        // 点击当前活动tab，不触发事件
        if (index == this.activedIndex) return
        this.activedIndex = index
        this.scrollByIndex()
        // 发送事件给父组件
        this.$emit('change', index, item)
      },
      // 控制滚动到位置
      scrollByIndex() {
        // 当前活动tab的布局信息，有tab菜单的width和left(为元素左边界到父元素左边界的距离)等信息
        let tabInfo = this.tabAllInfo[this.activedIndex]
        if (!tabInfo) return
        // 活动tab的宽度
        let tabWidth = tabInfo.width
        // 活动item的左边到tabs组件左边的距离，用item的left减去tabs的left
        let offsetLeft = tabInfo.left - this.parentLeft
        // 将活动的tabs-item移动到屏幕正中间，实际上是对scroll-view的移动
        let scrollLeft = offsetLeft - (this.componentWidth - tabWidth) / 2
        this.scrollLeft = scrollLeft < 0 ? 0 : scrollLeft
        console.log('scrollLeft', this.scrollLeft)
        // 当前活动item的中点点到左边的距离减去滑块宽度的一半，即可得到滑块所需的移动距离
        let left = tabInfo.left + tabInfo.width / 2 - this.parentLeft
        // 计算当前活跃item到组件左边的距离
        this.scrollBarLeft = left - uni.upx2px(60) / 2
        // 第一次移动滑块的时候，barFirstTimeMove为true，放到延时中将其设置false
        // 延时是因为scrollBarLeft作用于computed计算时，需要一个过程需，否则导致出错
        if (this.barFirstTimeMove == true) {
          setTimeout(() => {
            this.barFirstTimeMove = false
          }, 100)
        }
      },
    },
  }
</script>
<style lang="scss" scoped>
  scroll-view ::-webkit-scrollbar {
    display: none !important;
    width: 0 !important;
    height: 0 !important;
    -webkit-appearance: none;
    background: transparent;
  }

  .scroll-view {
    white-space: nowrap;
    width: 100%;
    height: 100%;
  }

  .ve-tabs {
    position: relative;
    height: 86rpx;
    white-space: nowrap;
    background: #fff;
    border-top: 1px solid #F7F8FA;
    border-bottom: 1px solid #F7F8FA;

    &-list {
      position: relative;
      width: 100%;
      height: 100%;
      display: flex;
    }

    &-item {
      position: relative;
      padding: 0 32rpx;
      flex: 1;
      flex-shrink: 0;
      font-weight: 400;
      line-height: 86rpx;
      font-size: 32rpx;
      font-family: PingFang SC-Regular, PingFang SC;
      color: #1d2129;
      text-align: center;

      .text {
        white-space: nowrap;
      }

      &.is-actived {
        color: #165dff;
        font-size: 32rpx;
        font-weight: 500;
        font-family: PingFang SC-Medium, PingFang SC;
      }

      .badge {
        position: absolute;
        left: calc(88% - 32rpx);
        top: 14rpx;
        height: 26rpx;
        line-height: 26rpx;
        background: linear-gradient(222deg, #ff7a66 0%, #ff5b60 100%);
        border-radius: 40rpx 40rpx 40rpx 0;
        padding: 0 10rpx;
        font-size: 20rpx;
        font-weight: 600;
        color: #ffffff;
      }
    }

    &-bar {
      position: absolute;
      bottom: 0;
      background-color: #3072f6;
      height: 4rpx;
      width: 48rpx;
    }
  }
</style>
